#include <buola/scene/crenderer.h>

#include "shaders/sFragmentShaderCoords.h"
#include "shaders/sVertexShaderCoords.h"
#include "shaders/sFragmentShader.h"
#include "shaders/sVertexShader.h"
#include "shaders/sSkinningShader.h"
#include "shaders/sTextureShader.h"

#include <buola/mat/transformations.h>

#include <buola/scene/crendercontext.h>
#include <buola/scene/cscene.h>
#include <buola/scene/ccamera.h>
#include <buola/gl/cvertexarray.h>

namespace buola { namespace scene {

using namespace gui;

CRenderer::CRenderer(EFlags pFlags)
    :   mFlags(pFlags)
    ,   mClearColor(0.498,0.7,0.9)
{
}

CRenderer::~CRenderer()
{

}

void CRenderer::SetCamera(PCamera pCam)
{
    mCamera=pCam;
}

void CRenderer::AddScene(PScene pScene)
{
    mScenes.push_back(pScene);
}

void CRenderer::SetClearColor(const img::CColor &pColor)
{
    mClearColor=pColor;
}

/*
void CRenderer::Render()
{
    if(!mScene||!mCamera) return;

    if(!mHaveProgram)
        CreateProgram();

    mProgram2.Use();

    mScene->Update();

    glPrimitiveRestartIndex(16777216);
    
    const C3DMatrix &lLookAt=mCamera->GetLookAtMatrix();
    C3DMatrix lProjection=mCamera->GetProjectionMatrix();

    if(mFlags&FLAG_FLIP)
    {
        for(int i=0;i<4;i++)
            lProjection(1,i)=-lProjection(1,i);
    }

    CRenderContext lContext;

    mProgram2.GetUniform("uMVPMatrix").SetValue(lProjection*lLookAt);
    mProgram2.GetUniform("uMVMatrix").SetValue(lLookAt);

//    mProgram.GetUniform("uPMatrix").SetValue(lProjection);
//    mProgram.GetUniform("uBoneInspect").SetValue(0);

    lContext.uSkinMatrices=mProgram.GetUniform("uSkinMatrices");

    lContext.mProjectionMatrix=lProjection;
    lContext.mViewMatrix=lLookAt;

    glEnable(GL_DEPTH_TEST);

//    gl::CUniform uTexture=mProgram.GetUniform("uTexture");
//    uTexture.SetValue(0);

    glVertexAttrib3d(1,0.5,0.5,0.5);

    lContext.uMVMatrix=mProgram2.GetUniform("uMVMatrix");
    lContext.uMVPMatrix=mProgram2.GetUniform("uMVPMatrix");

    mScene->Render(lContext);

    glDisable(GL_DEPTH_TEST);

    gl::CProgram::UseNone();
}
*/
void CRenderer::Render()
{
    std::vector<PTransform> lDummy;
    DoRender(lDummy,false);
}

void CRenderer::RenderWithTransformIndex(std::vector<PTransform> &pTransforms)
{
    DoRender(pTransforms,true);
}

void CRenderer::DoRender(std::vector<PTransform> &pTransforms,bool pTransformIndex)
{
    pTransforms.clear();
    
    if(mScenes.empty()||!mCamera) return;
    
    for(const PScene &lScene : mScenes)
    {
        lScene->Update();

        gl::CVertexArray lVertexArray;
        lVertexArray.Bind();
        
        glPrimitiveRestartIndex(16777216);
        
        const mat::CMat34d &lLookAt=mCamera->GetLookAtMatrix();
        mat::CMat4d lProjection=mCamera->GetProjectionMatrix();

        if(mFlags&FLAG_FLIP)
        {
            for(int i=0;i<4;i++)
                lProjection(1,i)=-lProjection(1,i);
        }

        CRenderContext lContext;
        
        lContext.mTransforms=std::move(pTransforms);
        lContext.mTechnique=nullptr;
        lContext.mProjectionMatrix=lProjection;
        lContext.mViewMatrix=lLookAt;
        lContext.mTransformIndex=pTransformIndex;

        glEnable(GL_DEPTH_TEST);
        glDisable(GL_BLEND);
        
        lScene->Render(lContext);

        glDisable(GL_DEPTH_TEST);

        gl::CProgram::UseNone();

        lVertexArray.Unbind();
        
        pTransforms=std::move(lContext.mTransforms);
    }
}

/*namespace gl*/ } /*namespace buola*/ }
